package com.ydlclass.aspect;

import com.ydlclass.annotation.Log;
import com.ydlclass.configuration.CustomObjectMapper;
import com.ydlclass.configuration.RedisTemplate;
import com.ydlclass.constants.Constants;
import com.ydlclass.entity.YdlLoginUser;
import com.ydlclass.entity.YdlOperLog;
import com.ydlclass.service.YdlOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

@Slf4j
@Aspect
@Component
public class LogAspect implements BeanFactoryAware {

    @Resource
    RedisTemplate redisTemplate;

    @Resource
    CustomObjectMapper objectMapper;

    @Resource
    YdlOperLogService ydlOperLogService;

    // 使用BeanFactoryAware注入一个BeanFactory，后续我们需要获取自身的bean时使用
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }


    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object result) {
        System.out.println("After===>浩");
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // === handleLog是经过代理的异步方法，不能直接简单的进行方法调用
        // === 正确的调用方法是，获取bean对象（这是个代理对象）调用
        LogAspect logAspect = beanFactory.getBean(LogAspect.class);
        logAspect.handleLog(joinPoint, controllerLog, null, result, request);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        LogAspect logAspect = beanFactory.getBean(LogAspect.class);
        logAspect.handleLog(joinPoint, controllerLog, e, null, request);
    }


    /**
     * 处理日志的逻辑
     *
     * @param joinPoint
     * @param controllerLog
     * @param e
     * @param result
     */
    @Async("ydlExecutor")
    public void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object result, HttpServletRequest request) {
        try {
            // 获取当前的用户
            Optional<YdlLoginUser> loginUser = redisTemplate.getObject(Constants.TOKEN_PREFIX + request.getHeader(Constants.HEAD_AUTHORIZATION), YdlLoginUser.class);

            loginUser.ifPresent(user -> {
                // ========数据库日志=========//
                YdlOperLog ydlOperLog = new YdlOperLog();
                // 设置一些基本信息
                ydlOperLog.setStatus(500);
                ydlOperLog.setOperIp(user.getIpaddr());
                ydlOperLog.setOperUrl(request.getRequestURI());
                ydlOperLog.setOperName(user.getYdlUser().getUserName());

                if (e != null) {
                    ydlOperLog.setStatus(200);
                    ydlOperLog.setErrormsg(e.getMessage().substring(0, 2000));
                }
                // 设置方法名称
                String className = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                ydlOperLog.setMethod(className + "." + methodName + "()");
                // 设置请求方式
                ydlOperLog.setRequestMethod(request.getMethod());
                // 处理设置注解上的参数
                // 设置action动作
                ydlOperLog.setBusinessType(controllerLog.businessType());
                // 设置标题
                ydlOperLog.setTitle(controllerLog.content());
                ydlOperLog.setOpertime(new Date());
                // 保存数据库
                ydlOperLogService.insert(ydlOperLog);
            });
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }
}